<template>
  <view class="components-container">
    <view class="category">
      <view>
        <view
          class="category-item"
          :class="{ active: index === active }"
          v-for="(dishCategory, index) in props.data"
          :key="index"
          @click="changeCategory(index)"
          >{{ dishCategory.name }}</view
        >
      </view>
    </view>
    <view class="items">
      <view class="items-rol">
        <view v-for="(dish, index) in currentDishList" :key="index" class="dish">
          <view class="dish-left" :class="{ signBox: dish.betterDisplay === 1 }">
            <span v-if="dish.betterDisplay === 1" class="sign">招牌</span>
            <image :src="dish.image" class="dish-image" />
          </view>
          <view class="dish-right">
            <view class="dish-name" :class="{ 'in-cart': findInCart(dish.id).length > 0 }">{{ dish.name }}</view>
            <view class="labels">
              <span v-for="(label, index) in dish.labels?.split(',')" :key="index" class="label">{{ label }}</span>
            </view>
            <view class="sale-info">
              <span class="monthly-sales">月售: {{ dish?.statisticsInfo?.count ?? '--' }}</span>
              <span class="score"
                >好评:
                <span class="score-value"
                  >{{ dish?.statisticsInfo?.goodRate ? dish.statisticsInfo.goodRate * 100 : '--' }}%</span
                ></span
              >
            </view>
            <view class="price-and-control">
              <view class="price">
                <span class="now-price"
                  >￥{{ dish.nowPrice + FlavorNameObj.getDishMinimumAppreciationSum(dish?.dishFlavorList) }}</span
                >
                <span class="old-price" v-if="dish.dishFlavorList == null || dish.dishFlavorList.length === 0"
                  >￥{{ dish.oldPrice + FlavorNameObj.getDishMinimumAppreciationSum(dish?.dishFlavorList) }}</span
                >
                <span v-else class="price-describe">起</span>
              </view>
              <!-- @click="addToCartForDish(dish)" -->
              <view
                :class="dish.dishFlavorList ? 'choose-and-add' : 'add'"
                class="choose-specification"
                @click="onCustomPlay($event, $refs, dish)"
                ref="c1"
                v-if="isDishSpecificationExists(dish) || !getChooseDishCountForCart(dish.id)"
              >
                {{ isDishSpecificationExists(dish) ? '选规格' : '+' }}
                <span class="specification-dish-buy-count" v-if="getChooseDishCountForCart(dish.id)">{{
                  getChooseDishCountForCart(dish.id)
                }}</span>
              </view>
              <view class="count-change" v-else>
                <!--这里使用find是因为这里肯定是这个菜品是没有口味规格的，就可以直接减-->
                <span class="reduce-btn" @click="specificationsCountPlusOrDown(cart.find((e) => e.id === dish.id)!, -1)"
                  >-</span
                >
                <span class="count">{{ getChooseDishCountForCart(dish.id)! }}</span>
                <span class="add-btn" @click="onCustomPlay($event, $refs, dish)">+</span>
              </view>
            </view>
          </view>
        </view>
      </view>
    </view>
    <select-specifications v-model:dish="currentSpecificationsDish" @add-to-cart="addToCartForSync" />
    <shopping-cart :cart="cart" v-model:cart-position="busPos" :dish="currentDishList" />
    <ball ref="ball" />
  </view>
</template>

<script setup lang="ts">
import type Dish from '@/types/entity/Dish'
import type DishCategory from '@/types/entity/DishCategory'
import { computed, provide, type Ref, ref, watch } from 'vue'
import SelectSpecifications from './select-specifications/index.vue'
import { CartItem, FlavorNameObj } from '@/types/entity/ShoppingCart'
import ShoppingCart from './shopping-cart/index.vue'
import $API from '@/api'
import Base64 from '@/utils/Base64'
import Ball from './ball/index.vue'
import $store from '@/store'
import TaskSyncActuator from '@/utils/TaskSyncActuator'
import onReShow from '@/helper/onReShow'

const props = defineProps({
  data: {
    type: Array<DishCategory>,
    default: []
  },
  shopId: {
    type: Number,
    require: true
  },
  positioningDishCategoryId: {
    type: Number,
    require: true
  },
  positioningDishId: {
    type: Number,
    require: false
  }
})
// 球
const busPos = ref({
  x: 0,
  y: 0
})
const { seatNumber, inviteCode } = $store.shop
// 加入购物车的球动画，在点击“+”或“选规格”时更新
let ballAnimationStart = () => {}
// 点击“+”或“选规格”时调用
function onCustomPlay(event: any, refs: any, dish: Dish) {
  // 更新加入购物车动画ballAnimationStart
  ballAnimationStart = () => {
    if (busPos.value?.x != null && busPos.value?.y != null) {
      refs.ball.touchOnGoods(event, busPos)
    } else {
      refs.ball.touchOnGoods(event)
    }
  }
  addToCartForDish(dish)
}
function specificationsCountPlusOrDown(cartItem: CartItem, increment: number) {
  const copyCartItem = JSON.parse(JSON.stringify(cartItem))
  copyCartItem.count = increment
  $store.cart.specificationsCountPlusOrDownEvent.emit(copyCartItem)
}
// 分类活跃项
const active = ref(0)
watch(
  () => props.data,
  (newVal, oldVal) => {
    if (newVal.length > 0 && props.positioningDishCategoryId) {
      active.value = newVal.findIndex((e) => e.id === props.positioningDishCategoryId)
    }
  }
)
// 购物车
const cart = $store.cart.cart.getRef() as Ref<CartItem[]>
// 初始化购物车
watch(
  () => props.shopId,
  (newVal) => {
    if (newVal != null) refreshShoppingCart()
  },
  { immediate: true }
)
async function refreshShoppingCart() {
  const result = await $API.shoppingCart.list({
    shopId: props.shopId!,
    seatNumber: seatNumber.getValue(),
    inviteCode: inviteCode.getValue()
  })
  if (result.success) cart.value = result.data
}
// 当页面重新显示时刷新购物车
onReShow(refreshShoppingCart)
seatNumber.watch(refreshShoppingCart)

// 监听刷新购物车刷新事件
$store.cart.shoppingCartRefreshEvent.on(refreshShoppingCart)
// 监听规格数据改变事件(num是1或-1分别表示count ++1与--1)
$store.cart.specificationsCountPlusOrDownEvent.on((cartItem) =>
  addToCartForSync(JSON.parse(JSON.stringify(cartItem)), false)
)
// 从购物车中找指定菜品的CartItem
function findInCart(dishId: number) {
  return (cart.value || []).filter((cartItem) => cartItem.id == dishId)
}

// 获取菜品规格唯一key-只用于前端从购物车中找
function getCartItemKey(inputCartItem: CartItem): string | null {
  // 对cartItem中的flavors根据id进行排序
  if (inputCartItem == null) return null
  let flavorKey = ''
  let dishKey = inputCartItem.id
  let flavors = inputCartItem.flavors
  if (flavors != null) {
    flavors.sort((e1, e2) => e1.id - e2.id)
    for (let flavor of flavors) {
      flavorKey += flavor.id + flavor.name
    }
  }
  return Base64.encode(`${dishKey}:${flavorKey}`)
}

// 从现在的购物车中获取指定相同的项
function popCartItemFromCart(inputCartItem: CartItem): {
  inCartCartItem: CartItem | null
  removeResidual: () => void
  index: number
} {
  const nullValue = { inCartCartItem: null, removeResidual: () => {}, index: -1 }
  if (cart.value == null) return nullValue
  for (let i = 0; i < cart.value.length; i++) {
    const inCartCartItem = cart.value[i]
    if (getCartItemKey(inCartCartItem) === getCartItemKey(inputCartItem)) {
      return {
        inCartCartItem,
        removeResidual: () => {
          cart.value.splice(i, 1)
        },
        index: i
      }
    }
  }
  return nullValue
}
// 添加到购物车

function addToCartForSync(cartItem: CartItem, isCallAnimation = true) {
  TaskSyncActuator.addSyncQueue(cartItem.id, () => addToCart(cartItem, isCallAnimation))
}
async function addToCart(cartItem: CartItem, isCallAnimation = true) {
  // 加入购物车的球动画开始
  if (isCallAnimation) ballAnimationStart()
  cartItem.shopId = props.data[0].shopId
  const { inCartCartItem, removeResidual, index } = popCartItemFromCart(cartItem)
  const newInCartCartItem = cartItemMerge(inCartCartItem, cartItem)
  if (newInCartCartItem == null) return
  const cartItemResult = await $API.shoppingCart.put({
    ...newInCartCartItem,
    seatNumber: seatNumber.getValue(),
    inviteCode: inviteCode.getValue()
  })
  if (cartItemResult == null || cartItemResult.data == null) {
    // 当返回空，说明该规格已经减完了
    // 之前弹出(popCartItemFromCart)与当前要添加的规格相同的cartItem推迟到现在删除，因为中间要请求网络，要很久
    removeResidual()
  } else {
    if (inCartCartItem == null || index < 0) {
      // 该规格在购物车中没有
      cart.value.push(cartItemResult.data)
    } else {
      // 数据改变了
      cart.value[index] = cartItemResult.data
    }
  }
  return true
}
function cartItemMerge(cartItem1: CartItem | null, cartItem2: CartItem | null): CartItem | null | undefined {
  if (cartItem1 == null || cartItem2 == null) return cartItem1 ?? cartItem2
  // 两个都不为空，进行合并
  const cartItem3 = new CartItem()
  cartItem3.id = cartItem1.id ?? cartItem2.id
  cartItem3.count = (cartItem1.count ?? 0) + (cartItem2.count ?? 0)
  cartItem3.price = (cartItem1.price ?? 0) + (cartItem2.price ?? 0)
  cartItem3.flavors = cartItem1.flavors ?? cartItem2.flavors
  cartItem3.shopId = cartItem1.shopId ?? cartItem2.shopId
  cartItem3.dishSpecificationKey = cartItem1.dishSpecificationKey ?? cartItem2.dishSpecificationKey
  return cartItem3
}
// 现在进行规格选择的菜品项
const currentSpecificationsDish = ref<Dish>()

// addToCartForDish
function addToCartForDish(dish: Dish) {
  // 如果没有口味，直接添加
  if (dish.dishFlavorList == null || dish.dishFlavorList.length === 0) {
    addToCartForSync({ id: dish.id, dishCategoryId: dish.dishCategoryId, price: dish.nowPrice, count: 1 })
  } else {
    // 有口味，弹出选择框，让用户选择再点击添加
    currentSpecificationsDish.value = dish
  }
}

// 根据当前分类computed得出当前要显示的菜品列表
const currentDishList = computed(() => props.data[active.value]?.dishList ?? [])
function changeCategory(index: number) {
  console.log('==changeCategory=')
  active.value = index
}
// 店铺所有菜品列表
const allDishList = computed(() => {
  const allDish = []
  for (let dish of props.data) {
    allDish.push(...(dish?.dishList ?? []))
  }
  return allDish
})
// 当dishId在购物车中有几个 使用filter而不是使用find是因为选择的口味不同时存在多个dishId一样的项，需要将他们的count算总和
function getChooseDishCountForCart(dishId: number): number {
  return (cart.value ?? []).filter((e) => e.id === dishId).reduce((pre, cur) => pre + cur.count, 0)
}
// 向下共享所有菜品的dishId->dish的Map形式
const allDishMap = computed(() => {
  const dishMap: { [key: string]: any } = {}
  for (let dish of allDishList.value) {
    dishMap[dish.id] = dish
  }
  return dishMap
})
function isDishSpecificationExists(dish: Dish) {
  return dish.dishFlavorList != null
}
provide('dishMap', allDishMap)
</script>

<style scoped lang="scss">
$category-width: 26%;

.components-container {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  height: 100%;
  background-color: #f5f5f5;
}

.category {
  width: $category-width;
  height: 100%;
  background: #f5f5f5;

  .category-item {
    width: 100%;
    height: 42px;
    font-size: 12px;
    font-weight: 500;
    border-right: 3px solid #5d4e1a00;
    border-left: 3px solid #f9d45000;
    text-align: center;
    box-sizing: border-box;
    line-height: 42px;
  }

  .active {
    border-left: 3px solid #f9d450;
    color: #f9d450;
    background-color: #fff;
  }
}

.items {
  width: 100% - $category-width;
  height: 100%;
  background-color: #fff;

  .items-rol {
    padding: 20rpx;
    box-sizing: border-box;

    .dish {
      display: flex;
      padding: 12rpx 0;
      width: 100%;
      border-bottom: 1rpx solid #87888a3b;

      .signBox {
        border: 2px solid #faca50;
        box-sizing: border-box;
      }

      .dish-left {
        position: relative;
        overflow: hidden;
        width: 200rpx;
        height: 200rpx;
        border-radius: 8%;

        .sign {
          position: absolute;
          padding: 6rpx 0;
          width: 36%;
          font-size: 22rpx;
          text-align: center;
          background-color: #faca50;
          border-bottom-right-radius: 10rpx;
        }

        .dish-image {
          width: 100%;
          height: 100%;
        }
      }

      .dish-right {
        flex-grow: 1;
        padding: 0 15rpx;
        box-sizing: border-box;

        .dish-name {
          height: 45rpx;
          font-size: 32rpx;
          font-weight: 700;
          line-height: 45rpx;
        }

        .in-cart {
          color: #fc810a;
        }

        .labels {
          .label {
            margin-right: 8rpx;
            padding: 5rpx 15rpx;
            height: 45rpx;
            font-size: 23rpx;
            border-radius: 10rpx;
            color: #484848;
            background: #f4f4f4;
            line-height: 44rpx;
          }
        }

        .sale-info {
          display: flex;
          justify-content: space-between;
          height: 45rpx;
          line-height: 45rpx;
          font-size: 25rpx;
          color: #87888a;
        }

        .score {
          .score-value {
            color: #e78719;
          }
        }

        .price-and-control {
          display: flex;
          justify-content: space-between;
          align-items: center;
          width: 100%;
          height: 60rpx;
          line-height: 60rpx;

          .price {
            font-size: 30rpx;
            font-weight: 700;
            color: #f54a26;

            .old-price {
              font-size: 20rpx;
              text-decoration: line-through;
              color: #87888a;
            }

            .price-describe {
              margin: 0 5rpx;
              font-size: 23rpx;
              color: #87888a;
            }
          }

          .add {
            width: 40rpx;
            height: 40rpx;
            font-size: 30rpx;
            font-weight: 700;
            border-radius: 50%;
            text-align: center;
            background-color: #f9d450;
            line-height: 40rpx;
            box-sizing: border-box;
          }

          .count-change {
            $count-change-box-height: 40rpx;

            display: flex;
            justify-content: space-between;
            align-items: center;
            height: $count-change-box-height;
            font-size: $count-change-box-height * 0.6;
            %btn-common {
              display: flex;
              justify-content: center;
              align-items: center;
              width: $count-change-box-height;
              height: $count-change-box-height;
              box-sizing: border-box;
            }

            .reduce-btn {
              border: 1px solid #fedd00;

              @extend %btn-common;
            }

            .count {
              padding: 0 22rpx;
            }

            .add-btn {
              color: #fff;
              background: #fedd00;

              @extend %btn-common;
            }
          }

          .choose-and-add {
            padding: 0rpx 19rpx;
            height: 45rpx;
            font-size: 25rpx;
            border-radius: 11rpx;
            background-color: #f9d450;
            line-height: 45rpx;
          }

          .choose-specification {
            position: relative;
            color: #1b1b1b;

            .specification-dish-buy-count {
              $specification-dish-buy-count-size: 30rpx;

              position: absolute;
              top: -$specification-dish-buy-count-size * 0.5;
              right: -$specification-dish-buy-count-size * 0.5;
              display: flex;
              justify-content: center;
              align-items: center;
              width: $specification-dish-buy-count-size;
              height: $specification-dish-buy-count-size;
              font-size: $specification-dish-buy-count-size * 0.7;
              border-radius: 50%;
              color: #fff;
              background: #fd5046;
              opacity: 0.9;
              box-sizing: border-box;
            }
          }
        }
      }
    }
  }
}
</style>
